package flowchart.app;

/*
 * Programming graphicxal user interfaces
 */
import java.util.*;
import flowchart.nodes.*;
import flowchart.editors.*;
import java.io.*;

/**
 * A class representing the model of a simple diagram.
 */

public class Diagram extends Observable {

    //private List editors;
    public ArrayList<GraphicsObjectEditor> editors;
    //picture is top level composite object
    //representing drawing
    private Picture pict;
    private GraphicsObjectEditorFactory factory;

    /**
     * Returns picture
     * @return
     */
    public Picture getPicture() {
        return pict;
    }

    /**
     * Sets picture
     * @param p
     */
    public void setPicture(Picture p) {
        pict = p;
        resetEditors();
        setChanged();
        notifyObservers(new Msg(Msg.PICTURE_INITIALIZED));
    }
    /**
     * Creates new picture
     */
    public void resetPicture() {
        setPicture(new Picture());
    }

    /**
     *  Add object to picture
     * @param obj
     */
    public void add(GraphicsObject obj) {
        pict.addObject(obj);
        resetEditors();
        setChanged();
        notifyObservers(new Msg(Msg.OBJECT_CREATED));
    }

    /**
     * remove object from picture
     * @param obj
     */
    public void removeObject(GraphicsObject obj) {
        if (obj != null) {
            pict.removeObject(obj);
            resetEditors();
            setChanged();
            notifyObservers(new Msg(Msg.OBJECT_DELETED));
        }
    }

    /**
     * Set grid
     * @param offset
     */
    public void setGrid(int offset) {
        pict.setGrid(offset);
        setChanged();
        if (offset > 0) {
            notifyObservers(new Msg(Msg.SNAP_TO_GRID_ACTIVE));
        } else {
            notifyObservers(new Msg(Msg.SNAP_TO_GRID_NOT_ACTIVE));
        }
    }

    /**
     * Selects object in given coordinates
     * @param x coordinate x
     * @param y coordinate y
     */
    public void SelectObject(int x, int y) {
        HitTestInfo hit = null;
        GraphicsObjectEditor se = null;

        Iterator<GraphicsObjectEditor> i = editors.iterator();
        while (i.hasNext()) {
            GraphicsObjectEditor editor = i.next();
            hit = editor.hitTest(x, y);

            if (hit.code != HitTestInfo.NOHIT) {
                se = editor;
                setChanged();
                Msg viesti = new Msg(Msg.OBJECT_SELECTED);
                viesti.put("editor", se);
                viesti.put("hit", hit);
                this.notifyObservers(viesti);
                break;
            }
        }

        if (se == null) {
            setChanged();
            this.notifyObservers(new Msg(Msg.OBJECT_DESELECTED));
        // }
        //return se;
        }
    }

   /**
    * Load picture from file
    * @param f
    */
    public void loadFromFile(java.io.File f) {

        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(f);
            in = new ObjectInputStream(fis);
            Picture loaded = (Picture) in.readObject();
            in.close();
            pict = loaded;
            resetEditors();
            setChanged();
            Msg viesti = new Msg(Msg.PICTURE_LOADED);
            viesti.put("file", f);
            notifyObservers(viesti);


        //TODO huono tapa, exeptionin nielasu
        } catch (Exception ex) {
            setChanged();
            notifyObservers(new Msg(Msg.FILE_ERROR));
        }
    }



    /**
     * Saves picture to file
     * @param f
     */
    public void SaveToFile(java.io.File f) {
        FileOutputStream fos = null;
        ObjectOutputStream out = null;

        try {
            File temp = f;
            String ext = null;
            String s = temp.getName();
            int i = s.lastIndexOf('.');

            if (i > 0 && i < s.length() - 1) {
                ext = s.substring(i + 1).toLowerCase();
            }
            // chech if extension is ok
            String extension = ext;
            if (extension == null || (!extension.equals("wtf"))) {
                //add extensioni
                s += ".wtf";
                f = new File(temp.getParentFile(), s);

            } else {
                f = new File(temp.getParentFile(), temp.getName());
            }
            fos = new FileOutputStream(f);
            out = new ObjectOutputStream(fos);
            out.writeObject(getPicture());
            out.close();
            setChanged();

            Msg viesti = new Msg(Msg.PICTURE_SAVED);
            viesti.put("file", f);
            notifyObservers(viesti);

        //TODO huono tapa, exceptionin nielasu
        } catch (IOException ex) {
            notifyObservers(new Msg(Msg.FILE_ERROR));
        // ex.printStackTrace();
        }



    }
    /**
     * Constructor
     */
    public Diagram() {

        factory = new GraphicsObjectEditorFactory();
        editors = new ArrayList();

        factory.registerEditorClass(
                (new GraphicsCircle(100, 100)).getClass(),
                (new CircleGraphicsObjectEditor()).getClass());

        factory.registerEditorClass(
                (new GraphicsConnector("a", new GraphicsNode("a", 100, 100), new GraphicsNode("b", 100, 100))).getClass(),
                (new ConnectorEditor()).getClass());

        this.resetPicture();

    }
    /**
     * Reset editors
     */
    protected void resetEditors() {
        editors.clear();
        for (int i = pict.getCount() - 1; i >= 0; i--) {
            GraphicsObject obj = pict.getObject(i);
            GraphicsObjectEditor editor = factory.getEditor(obj);
            editors.add(editor);
        }
    }

    /**
     * Creates new rectangle node
     * @param title
     * @param x
     * @param y
     */
    public void createNode(String title, int x, int y) {
        //creating graphics node and putting it to thr composite model
        GraphicsNode gnode = new GraphicsNode(title, x, y);
        pict.addObject(gnode);
        resetEditors();
        setChanged();
        notifyObservers(new Msg(Msg.NODE_CREATED));

    }

    /**
     * Creates new circle node
     * @param title
     * @param x
     * @param y
     */
    public void createCircle(String title, int x, int y) {
        //creating graphics node and putting it to thr composite model
        GraphicsCircle gcircle = new GraphicsCircle(title, x, y);
        pict.addObject(gcircle);
        resetEditors();
        setChanged();
        notifyObservers(new Msg(Msg.CIRCLE_CREATED));

    }
    /**
     * Creates new connector
     * @param title
     * @param n1
     * @param n2
     */
    public void createConnector(String title, GraphicsObject n1, GraphicsObject n2) {
        GraphicsConnector gc = new GraphicsConnector(title, n1, n2);
        pict.addObject(gc);
        resetEditors();
        setChanged();
        notifyObservers(new Msg(Msg.CONNECTOR_CREATED));
    }

    /**
     * Sets title for object
     * @param o
     * @param title
     */
    public void setTitle(GraphicsObject o, String title) {
        if (o != null) {
            o.setTitle(title);
            setChanged();
            notifyObservers(new Msg(Msg.OBJECT_TITLE_CHANGED));
        }
    }


    /**
     * message class that diagram sends to observers
     */
    public static class Msg extends HashMap {

        public final static String OBJECT_CREATED = "Object created";
        public final static String OBJECT_DELETED = "Object deleted";
        public final static String OBJECT_SELECTED = "Object selected";
        public final static String OBJECT_DESELECTED = "Object deselected";
        public final static String OBJECT_TITLE_CHANGED = "Title changed";
        public final static String PICTURE_LOADED = "Picture loaded";
        public final static String PICTURE_SAVED = "Picture saved";
        public final static String FILE_ERROR = "File error";
        public final static String PRINT_ERROR = "Print error";
        public final static String PICTURE_INITIALIZED = "Picture initialized";
        public final static String NODE_CREATED = "Node Created";
        public final static String CONNECTOR_CREATED = "Connector created";
        public final static String CIRCLE_CREATED = "Circle created";
        public final static String SNAP_TO_GRID_ACTIVE = "Snap to grid active";
        public final static String SNAP_TO_GRID_NOT_ACTIVE = "Snap to grid not active";
        private String message;

        Msg(String v) {
            message = v;
        }
        /**
         * set message
         * @param message
         */
        public void setMessage(String message) {
            this.message = message;
        }
        /**
         * Return message
         * @return
         */
        public String getMessage() {
            return message;
        }
        /**
         * Return true if messaga equals parameter string
         * @param c
         * @return
         */
        public boolean equals(String c) {
            return message.equals(c);
        }
    }
}
